<template>
    <div class="full-calendar-body">
      <div class="weeks">
        <strong class="week" v-for="(week, index) in weekNames" :key="index">{{ week }}</strong>
      </div>
      <div class="dates" ref="datesRef">
        <div class="dates-events">
          <div class="events-week" v-for="(week, index) in currentDates" :key="index">
            <div
              class="events-day"
              v-for="(day, index) in week"
              :key="index"
              :class="{
                today: day.isToday,
                'not-cur-month': !day.isCurMonth,
                eventBg: day.events.length > 0,
                clickDay: day.date === selectedDay,
              }"
              @click.stop="eventClick(day.date, day.events, $event)"
            >
              <div class="event-box">
                <p
                  class="event-item"
                  v-for="(event, index) in day.events"
                  :key="index"
                  v-show="event.cellIndex <= eventLimit"
                  :class="[
                    classNames(event.cssClass),
                    {
                      'is-start': isStart(event.start, day.date),
                      'is-end': isEnd(event.end, day.date),
                      'is-opacity': !event.isShow,
                      'is-work': event.holidayFlag / 1 === 1,
                    },
                  ]"
                >
                  {{ isBegin(event, day.date, day.weekDay) }}
                  <span v-if="event.holidayName">{{ event.holidayName }}</span>
                </p>
                <p
                  v-if="day.events.length > eventLimit"
                  class="more-link"
                  @click.stop="selectThisDay(day, $event)"
                >
                  +{{ day.events[day.events.length - 1].cellIndex - eventLimit }} more
                </p>
              </div>
              <p
                class="day-number"
                :class="{
                  eventDay: day.events.length > 0,
                  'iswork': day.events.length > 0 && day.events[0].holidayFlag / 1 === 1,
                }"
              >
                {{ day.monthDay }}
              </p>
            </div>
          </div>
        </div>
        <div
          class="more-events"
          v-show="showMore"
          :style="{ left: morePos.left + 'px', top: morePos.top + 'px' }"
        >
          <div class="more-header">
            <span class="title">{{ moreTitle(selectDay.date) }}</span>
            <span class="close" @click.stop="showMore = false">x</span>
          </div>
          <div class="more-body">
            <ul class="body-list">
              <li
                v-for="(event, index) in selectDay.events"
                :key="index"
                :id="String(index)"
                v-show="event.isShow"
                class="body-item"
                @click="eventClick(event, $event)"
              >
                {{ eventTitle }}
              </li>
            </ul>
          </div>
        </div>
        <slot name="body-card"></slot>
      </div>
    </div>
  </template>
  
  <script lang="ts" setup>
  import { ref, onMounted, watch, computed } from 'vue';
  import * as dateFunc from '../utils/dateFunc';
  const emit = defineEmits(['dayclick', 'eventclick', 'moreclick']);
  
  // 定义 props 类型
  interface Props {
    currentDate: any;
    events: any[];
    weekNames: string[];
    monthNames: any;
    firstDay: any;
  }
  
  const props = withDefaults(defineProps<Props>(), {
    weekNames: () => [],
  });
  
  // 定义状态
  const weekMask = ref([1, 2, 3, 4, 5, 6, 7]);
  const isLismit = ref(true);
  const eventLimit = ref(1);
  const showMore = ref(false);
  const morePos = ref({ top: 0, left: 0 });
  const selectDay = ref({});
  const eventTitle = ref('休');
  const selectedDay = ref('');
  const datesRef = ref<HTMLElement | null>(null);
  
  // 处理事件
  const handleEvents = () => {
    props.events?.forEach((item, index) => {
      item._id = item.id || index;
      item.start = item.holidayDate;
      item.end = item.start || item.holidayDate;
    });
  };
  
  // 监听 props 变化
  watch(
    () => props.weekNames,
    (val) => {
      console.log('watch weekNames', val);
    }
  );
  
  watch(
    () => props.events,
    (newVal, oldVal) => {
      if (newVal.length > 0) {
        handleEvents();
      }
    },
    { deep: true }
  );
  
  // 计算属性
  const currentDates = computed(() => {
    return getCalendar();
  });
  
  // 判断是否是事件开始
  const isBegin = (event: any, date: Date, index: number) => {
    let st = new Date(event.start);
    if (index === 0 || st.toDateString() === date.toDateString()) {
      if (event.holidayFlag == 1) {
        return '休';
      } else if (event.holidayFlag == 0) {
        return '班';
      }
    }
    return '假';
  };
  
  // 更多标题
  const moreTitle = (date: Date) => {
    let dt = new Date(date);
    return `${props.weekNames[dt.getDay()]}, ${props.monthNames[dt.getMonth()]}${dt.getDate()}`;
  };
  
  // 处理类名
  const classNames = (cssClass: string | string[]) => {
    if (!cssClass) return '';
    if (typeof cssClass === 'string') return cssClass;
    if (Array.isArray(cssClass)) return cssClass.join(' ');
    return '';
  };
  
  // 获取日历数据
  const getCalendar = () => {
    let now = new Date();
    let current = new Date(props.currentDate);
  
    let startDate = dateFunc.getStartDate(current);
  
    let curWeekDay = startDate.getDay();
    let diff = parseInt(props.firstDay) - curWeekDay;
    diff = diff > 0 ? diff - 7 : diff;
  
    startDate.setDate(startDate.getDate() + diff);
    let calendar: any[] = [];
  
    for (let perWeek = 0; perWeek < 6; perWeek++) {
      let week: any[] = [];
  
      for (let perDay = 0; perDay < 7; perDay++) {
        week.push({
          monthDay: startDate.getDate(),
          isToday: now.toDateString() === startDate.toDateString(),
          isCurMonth: startDate.getMonth() === current.getMonth(),
          weekDay: perDay,
          date: new Date(startDate),
          events: slotEvents(startDate),
        });
        startDate.setDate(startDate.getDate() + 1);
      }
  
      calendar.push(week);
    }
    return calendar;
  };
  
  // 获取当天的事件
  const slotEvents = (date: Date) => {
    let cellIndexArr: any[] = [];
    let thisDayEvents = props.events.filter((day) => {
      let dt = new Date(day.start);
      let st = new Date(dt.getFullYear(), dt.getMonth(), dt.getDate());
      let ed = day.end ? new Date(day.end) : st;
      return date >= st && date <= ed;
    });
  
    thisDayEvents.sort((a, b) => {
      if (!a.cellIndex) return 1;
      if (!b.cellIndex) return -1;
      return a.cellIndex - b.cellIndex;
    });
  
    for (let i = 0; i < thisDayEvents.length; i++) {
      thisDayEvents[i].cellIndex = thisDayEvents[i].cellIndex || i + 1;
      thisDayEvents[i].isShow = true;
      if (thisDayEvents[i].cellIndex === i + 1 || i > 2) continue;
      thisDayEvents.splice(i, 0, {
        title: 'holder',
        cellIndex: i + 1,
        start: dateFunc.format(date, 'yyyy-MM-dd', undefined),
        end: dateFunc.format(date, 'yyyy-MM-dd', undefined),
        isShow: false,
      });
    }
  
    return thisDayEvents;
  };
  
  // 判断是否是事件开始日期
  const isStart = (eventDate: any, date: Date) => {
    let st = new Date(eventDate);
    return st.toDateString() === date.toDateString();
  };
  
  // 判断是否是事件结束日期
  const isEnd = (eventDate: any, date: Date) => {
    let ed = new Date(eventDate);
    return ed.toDateString() === date.toDateString();
  };
  
  // 选择当天
  const selectThisDay = (day: any, jsEvent: Event) => {
    selectDay.value = day;
    showMore.value = true;
    morePos.value = computePos(jsEvent.target as HTMLElement);
    morePos.value.top -= 100;
    let events = day.events.filter((item: any) => {
      return item.isShow === true;
    });

    emit('moreclick', day.date, events, jsEvent);
  };
  
  // 计算位置
  const computePos = (target: HTMLElement) => {
    let eventRect = target.getBoundingClientRect();
    let pageRect = datesRef.value?.getBoundingClientRect();
    return {
      left: eventRect.left - (pageRect?.left || 0),
      top: eventRect.top + eventRect.height - (pageRect?.top || 0),
    };
  };
  
  // 点击日期
  const dayClick = (day: any, event: Event) => {
    emit('dayclick', day, event);
  };

  // 点击事件
  const eventClick = (date: any, event: any, jsEvent: Event) => {
    selectedDay.value = date;
    jsEvent.stopPropagation();
    let pos = computePos(jsEvent.target as HTMLElement);
    emit('eventclick', date, event, jsEvent, pos);
  };
  
  // 组件挂载时处理事件
  onMounted(() => {
    handleEvents();
  });
  </script>
  
  <style lang="scss">
  .full-calendar-body {
    margin-top: 20px;
    .weeks {
      height: 30px;
      line-height: 30px;
      display: flex;
      border-top: 1px solid #dfe6ec;
      border-bottom: 1px solid #dfe6ec;
      border-left: 1px solid #dfe6ec;
      .week {
        flex: 1;
        text-align: center;
        border-right: 1px solid #dfe6ec;
      }
    }
    .dates {
      position: relative;
      .week-row {
        // width: 100%;
        // position:absolute;
        border-left: 1px solid #dfe6ec;
        display: flex;
        .day-cell {
          flex: 1;
          min-height: 90px;
          padding: 4px;
          border-right: 1px solid #dfe6ec;
          border-bottom: 1px solid #dfe6ec;
          .day-number {
            text-align: center;
            font-size: 20px;
            font-weight: bold;
            height: 100%;
            display: flex;
            justify-content: center;
            align-items: center;
          }
          &.today {
            background-color: #fcf8e3;
          }
          &.not-cur-month {
            .day-number {
              color: rgba(0, 0, 0, 0.24);
            }
          }
        }
      }
      .dates-events {
        // position:absolute;
        position: relative;
        top: 0;
        left: 0;
        z-index: 1;
        width: 100%;
        border-left: 1px solid #dfe6ec;
        .events-week {
          display: flex;
          border-bottom: 1px solid #dfe6ec;
          .events-day:hover {
            background-color: rgba(102, 153, 255, 0.1);
          }
          .events-day {
            cursor: pointer;
            flex: 1;
            min-height: 90px;
            overflow: hidden;
            text-overflow: ellipsis;
            border-right: 1px solid #dfe6ec;
            position: relative;
            &.eventBg {
              background-color: #f5f5f5;
            }
            &.clickDay {
              background-color: rgba(102, 153, 255, 0.2);
            }
            .day-number {
              text-align: center;
              font-size: 20px;
              font-weight: bold;
              height: 100%;
              margin-top: -30px;
              display: flex;
              justify-content: center;
              align-items: center;
  
              &.eventDay {
                color: #e02d2d;
              }
              &.iswork {
                color: #000;
              }
            }
            &.today {
              background-color: #fcf8e3;
            }
            &.not-cur-month {
              pointer-events: none;
              .day-number {
                color: rgba(0, 0, 0, 0.24);
              }
            }
            .event-box {
              height: 30px;
  
              .event-item {
                cursor: pointer;
                text-align: center;
                font-size: 18px;
                font-weight: bold;
                background-color: #f43;
                margin-bottom: 2px;
                color: #fff;
                // padding: 2px 5px;
                height: 100%;
                line-height: 30px;
                width: 30px;
  
                white-space: nowrap;
                overflow: hidden;
                text-overflow: ellipsis;
  
                &.is-start {
                  margin-left: 4px;
                  // border-top-left-radius:4px;
                  // border-bottom-left-radius:4px;
                }
                &.is-end {
                  margin-right: 4px;
                  // border-top-right-radius:4px;
                  // border-bottom-right-radius:4px;
                }
                &.is-opacity {
                  opacity: 0;
                }
                &.is-work {
                  background-color: #969799;
                  span {
                    color: #999;
                  }
                }
                span {
                  position: absolute;
                  bottom: 5px;
                  left: 0;
                  right: 0;
                  margin: auto;
                  color: #e02d2d;
                  font-size: 14px;
                }
              }
              .more-link {
                cursor: pointer;
                // text-align: right;
                padding-left: 8px;
                padding-right: 2px;
                color: rgba(0, 0, 0, 0.38);
                font-size: 14px;
              }
            }
          }
        }
      }
      .more-events {
        // position:absolute;
        position: relative;
        width: 150px;
        z-index: 2;
        border: 1px solid #eee;
        box-shadow: 0 2px 6px rgba(0, 0, 0, 0.15);
        .more-header {
          background-color: #eee;
          padding: 5px;
          display: flex;
          align-items: center;
          font-size: 14px;
          .title {
            flex: 1;
          }
          .close {
            margin-right: 2px;
            cursor: pointer;
            font-size: 16px;
          }
        }
        .more-body {
          height: 140px;
          overflow: hidden;
          .body-list {
            height: 120px;
            padding: 5px;
            overflow: auto;
            background-color: #fff;
            .body-item {
              cursor: pointer;
              font-size: 12px;
              background-color: #c7e6fd;
              margin-bottom: 2px;
              color: rgba(0, 0, 0, 0.87);
              padding: 0 0 0 4px;
              height: 18px;
              line-height: 18px;
              white-space: nowrap;
              overflow: hidden;
              text-overflow: ellipsis;
            }
          }
        }
      }
    }
  }
  </style>    